#pragma once

#include <memory.h>

#include "command.hpp"
#include "components.hpp"

#include "baseCommandReceiver.hpp"

#include "events.hpp"
#include "qbe/src/ecs/event.hpp"

#include <entt/entt.hpp>

#include "qbe/src/layout/layout_cmd.hpp"

namespace qbe::undoRedoT {

// 添加对象
template <typename T>
class AddObjCR : public BaseCommandReceiver {
 public:
  AddObjCR();
  ~AddObjCR();

  virtual bool Action(bool bUndo) override;

  // 准备数据
  void Prepare(qlib::Library::s_ptr _library, entt::registry* _reg, entt::entity _entity, ecs::EventWriter<Event>& _event_writer,
               layout::LayoutCmd* _cmd, bool _use_cmd = true);

 private:
  entt::registry* reg = nullptr;
  entt::entity entity = entt::null;
  qlib::Library::s_ptr library = nullptr;

  bool use_cmd;

  std::shared_ptr<T> ptr;
  ecs::EventWriter<Event>* event_writer;
  layout::LayoutCmd* cmd;

  bool first{true};
  bool redo{false};
  bool undo{false};
};

template <typename T>
AddObjCR<T>::AddObjCR() {}

template <typename T>
AddObjCR<T>::~AddObjCR() {
  // 判断是否执行过
  if (!first) {
    // 尝试获取reg和InUndoRedoStack组件
    if (reg != nullptr && reg->try_get<InUndoRedoStack>(entity) != nullptr) {
      // 获取组件
      auto& com = reg->get<InUndoRedoStack>(entity);
      auto& undoRedo_count = com.num;

      // 此命令析构，因此实体在UndoRedo模块计数-1
      undoRedo_count--;

      // 如果计数为0，且实体有删除标识，则可以实际销毁它
      if (undoRedo_count == 0 && reg->any_of<ObjDeleteTag>(entity)) {
        // reg->destroy(entity);
      }
    }
  }
}

// 准备数据
template <typename T>
void AddObjCR<T>::Prepare(qlib::Library::s_ptr _library, entt::registry* _reg, entt::entity _entity, ecs::EventWriter<Event>& _event_writer,
                          layout::LayoutCmd* _cmd, bool _use_cmd) {
  library = _library;
  reg = _reg;
  entity = _entity;

  // 事件写入器和LayoutCmd
  event_writer = &_event_writer;
  cmd = _cmd;

  // 备份内核对象指针
  ptr = reg->get<std::shared_ptr<T>>(entity);
  redo = true;

  // 此参数表示对象是否使用LayoutCmd
  use_cmd = _use_cmd;
}

// 执行
template <typename T>
bool AddObjCR<T>::Action(bool bUndo) {
  // 必须在对应状态执行对应逻辑
  if ((bUndo && !undo) || (!bUndo && !redo))
    return false;

  if (!reg->valid(entity)) {
    return false;
  }

  if constexpr (std::is_same_v<qlib::Reference, T>) {
    if (ptr->cell == nullptr) {
      return false;
    }

    bool have_cell = false;
    auto check_cell = ptr->cell;

    for (auto cell : library->cells) {
      if (cell == check_cell) {
        have_cell = true;
      }
    }

    if (!have_cell)
      return false;
  }

  // 如果第一次执行
  if (first) {
    // 尝试获取UndoRedo计数组件，没有则添加
    if (reg->try_get<InUndoRedoStack>(entity) == nullptr) {
      reg->emplace<InUndoRedoStack>(entity);
    }

    // 获取一定存在的UndoRedo组件，将计数+1
    auto& in = reg->get<InUndoRedoStack>(entity);
    in.num++;

    // 标识非第一次执行
    first = false;
    // reg->emplace_or_replace<ObjAddTag>(entity);
    // event_writer->send({Code::ObjAdd});
    // cmd->add<std::shared_ptr<T>>(entity);
  } else if (bUndo) {
    // 撤销添加：移除实体上的内核指针
    // reg->remove<std::shared_ptr<T>>(entity);

    // 添加UndoRedo删除标记，并发送事件
    reg->emplace_or_replace<ObjDeleteTag>(entity);
    // event_writer->send({Code::ObjDelete});

    /*
      调用LayoutCmd移除
        对单一对象:   use_cmd为true，直接调用LayoutCmd.remove(entt::entity)
        对批量对象:   use_cmd为false，额外添加一条命令，专用于调用LayoutCmd.remove(std::vector<entt::entity>)
    */
    if (use_cmd)
      cmd->remove(entity);
  } else {
    // 恢复添加：将备份的内核指针绑定到实体上
    reg->emplace<std::shared_ptr<T>>(entity, ptr);

    // 绑定对应标记并发送事件
    reg->emplace_or_replace<ObjAddTag>(entity);
    // event_writer->send({Code::ObjAdd});

    // 调用LayoutCmd添加
    if (use_cmd)
      cmd->add(entity, ptr);
  }

  undo = !undo;
  redo = !redo;

  return true;
}

}  // namespace qbe::undoRedoT
